package org.example.hw.controller;

import java.util.List;

import javax.servlet.http.HttpSession;
import javax.validation.Valid;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.example.hw.dto.user.ChangePasswordDto;
import org.example.hw.dto.user.EditUserDto;
import org.example.hw.dto.user.LoginUserDto;
import org.example.hw.dto.user.RegisterUserDto;
import org.example.hw.dto.user.ViewUserDto;
import org.example.hw.model.User;
import org.example.hw.service.UserService;
import org.example.hw.util.mapping.UserMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class UserController {

	@Autowired
	private UserService userService;

	private static final Logger LOGGER = LogManager.getLogger(UserController.class);

	@RequestMapping(value = "/users", method = RequestMethod.GET)
	public String handleUsersGet(Model model) {

		LOGGER.debug("Handle user get controller invoked.");

		List<User> usersList = userService.findAll();
		model.addAttribute("usersList", usersList);

		LOGGER.debug("Users fetched, returning to view all users page...");

		return "users";
	}

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String handleUserLoginGet(HttpSession session, Model model) {

		User user = (User) session.getAttribute("user");

		if (user != null) {
			LOGGER.info("Already logged in, returning to home page...");

			return "home";
		}

		LOGGER.debug("Handle user login get controller invoked.");

		model.addAttribute("loginUserDto", new LoginUserDto());

		LOGGER.debug("Sending to login page...");

		return "login";
	}

	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String handleUserLoginPost(@Valid @ModelAttribute("loginUserDto") LoginUserDto loginUserDto,
			BindingResult result, HttpSession session) {

		LOGGER.debug("Handle user login post controller invoked.");

		if (result.hasErrors()) {
			LOGGER.info("Binding error.", result);

			return "login";
		}

		User user = userService.login(loginUserDto);

		if (user == null) {
			LOGGER.debug("Returning to login page...");

			return "login";
		}

		session.setAttribute("user", user);

		LOGGER.debug("Returning to home page...");

		return "redirect:/home"; // TODO: treba na home controller, verovatno je potrebno staviti "/home".
	}

	@RequestMapping(value = "/register", method = RequestMethod.GET)
	public String handleUserRegisterGet(HttpSession session, Model model) {

		User user = (User) session.getAttribute("user");

		if (user != null) {
			LOGGER.info("Already logged in, returning to home page...");

			return "home";
		}

		LOGGER.debug("Handle user register get controller invoked.");

		model.addAttribute("registerUserDto", new RegisterUserDto());

		LOGGER.debug("Sending to register page...");

		return "register";
	}

	@RequestMapping(value = "/register", method = RequestMethod.POST)
	public String handleUserRegisterPost(@Valid @ModelAttribute("registerUserDto") RegisterUserDto registerUserDto,
			BindingResult result, HttpSession session, Model model) {

		LOGGER.debug("Handle user register post controller invoked.");

		if (result.hasErrors()) {
			LOGGER.info("Binding error.", result);

			return "register";
		}

		if (!registerUserDto.getPassword().equals(registerUserDto.getPasswordRetyped())) {
			LOGGER.info("Password mismatch, returning to register page...");
			result.rejectValue("passwordRetyped", "registerUserDto.passwordMismatch");

			return "register";
		}

		User user = UserMapping.mapAdd(registerUserDto);
		User registered = userService.register(user);

		if (registered == null) {
			LOGGER.info("Error while saving registered user, returning to register page...");
			result.rejectValue("username", "registerUserDto.usernameNotUnique");

			return "register";
		}

		session.setAttribute("user", registered);

		LOGGER.debug("User registered, redirecting to view user page...");

		// redirect because of post-get-post pattern, to avoid duplicate form submission
		return "redirect:/" + registered.getUsername();
	}

	@RequestMapping(value = "/changePassword", method = RequestMethod.GET)
	public String handleUserChangePasswordGet(HttpSession session, Model model) {

		LOGGER.debug("Handle user change password get controller invoked.");

		User user = (User) session.getAttribute("user");

		if (user == null) {
			LOGGER.info("No one is logged, returning to home page...");

			return "login";
		}

		model.addAttribute("changePasswordDto", new ChangePasswordDto());

		LOGGER.debug("Sending to change password page...");

		return "changePassword";

	}

	@RequestMapping(value = "/changePassword", method = RequestMethod.POST)
	public String handleUserChangePasswordPost(
			@Valid @ModelAttribute("changePasswordDto") ChangePasswordDto changePasswordDto,
			BindingResult result, HttpSession session, Model model) {

		LOGGER.debug("Handle user change password post controller invoked.");

		if (result.hasErrors()) {
			LOGGER.info("Binding error.");

			return "changePassword";
		}

		User user = (User) session.getAttribute("user");

		if (user == null) {
			LOGGER.info("No one is logged, returning to home page...");

			return "login";
		}

		if (!user.getPassword().equals(changePasswordDto.getOldPassword())) {
			LOGGER.info("Wrong old password.");

			result.rejectValue("oldPassword", "changePasswordDto.wrongOldPassword");
			return "changePassword";
		}

		if (!changePasswordDto.getNewPassword().equals(changePasswordDto.getNewPasswordRetyped())) {
			LOGGER.info("New password mismatch.");

			result.rejectValue("newPassword", "changePasswordDto.newPasswordMismatch");
			return "changePassword";
		}

		UserMapping.mapEdit(changePasswordDto, user);
		user = userService.save(user);

		session.setAttribute("user", user);

		return "redirect:/" + user.getUsername();
	}

	@RequestMapping(value = "/logout", method = RequestMethod.GET)
	public String handleUserLogoutGet(HttpSession session) {

		LOGGER.debug("Handle user logout get controller invoked. ");

		User user = (User) session.getAttribute("user");

		LOGGER.info("Logging out user: " + user);

		session.invalidate();

		LOGGER.info("User logged out: " + user);
		LOGGER.debug("Sending to home page...");

		return "home";
	}

	@RequestMapping(value = "/{username}", method = RequestMethod.GET)
	public String handleUserViewGet(@PathVariable("username") String username, Model model) {

		User user = userService.findByUsername(username);

		if (user == null) {
			// TODO: kako da se vrati bas na prethodnu stranu, na kojoj je bio
			return "home";
		}

		ViewUserDto viewUserDto = new ViewUserDto(user);
		model.addAttribute("viewUserDto", viewUserDto);

		return "viewUser";
	}

	@RequestMapping(value = "/edit", method = RequestMethod.GET)
	public String handleUserEditGet(Model model, HttpSession session) {

		LOGGER.debug("Handle user edit get controller invoked.");

		User sessionUser = (User) session.getAttribute("user");

		if (sessionUser == null) {
			LOGGER.info("No user in session, sending to home...");

			return "login";
		}

		LOGGER.info("Request for edit granted, sending to edit user page...");

		EditUserDto editUserDto = new EditUserDto(sessionUser);
		model.addAttribute("viewUserDto", editUserDto);

		return "editUser";
	}

	@RequestMapping(value = "/edit", method = RequestMethod.POST)
	public String handleUserEditPost(@Valid @ModelAttribute("editUserDto") EditUserDto editUserDto,
			BindingResult result, Model model, HttpSession session) {

		LOGGER.info("Handle user edit post controller invoked.");

		if (result.hasErrors()) {
			LOGGER.info("Binding error. ", result);

			return "editUser";
		}

		LOGGER.info("Editing user...");

		User user = (User) session.getAttribute("user");

		UserMapping.mapEdit(editUserDto, user);

		user = userService.save(user);

		session.setAttribute("user", user);

		return "redirect:/" + user.getUsername();
	}

	@RequestMapping(value = "/delete")
	public String handleUserDeleteGet(HttpSession session) {

		LOGGER.debug("Handle user delete controller invoked");

		User sessionUser = (User) session.getAttribute("user");
		session.invalidate();

		userService.delete(sessionUser.getUsername());

		LOGGER.info("User deactivated: " + sessionUser);

		return "home";
	}

	@RequestMapping(value = "/checkUsername", method = RequestMethod.POST)
	public @ResponseBody String handleAjaxCheckUsername(@RequestParam("username") String username) {

		LOGGER.debug("Handle ajax check username controller invoked.");

		User user = userService.findByUsername(username);

		if (user != null) {
			LOGGER.info("Username already taken");

			return Boolean.toString(false);
		}

		LOGGER.info("Username is available.");

		return Boolean.toString(true);
	}

}
